OPTIONS LINESIZE=80;
TITLE 'example';


/***
Macro to set exclusion of trials, paracels or sessions.
*/
/**For all the macros, these are the variables:
parcelVar - Variable that indicates the parcel.
trlRT - The latency variable 
trlErr - the error variable. excludeErr - if 1 then exclude error trials.
**NOTE: error is 1 and correct is 0. But some tasks have more complicated error coding, so these will either need
their own macros or a macro around this macro.;
trlExc - output variable that will indicate whether the trial is excluded. 
sidExc - output variable that will indicate whether the session is excluded.
prcExc - output variable that will indicate whether the parcel is excluded.
**IF INSTEAD OF A VARIABLE NAME, ONE OF THE ABOVE 3 IS SET TO -1, THEN THE MACRO SHOULD NOT SET EXCLUSION ON THAT LEVEL.
minRT, maxRT - minimum and maximum acceptable latency.
sidErrRate, sidFastRate, sidSlowRate, sidFastSlowRate, sidFastSlowErrRate, sidNGoodTrials - 
acceptable rates in a a session of error trials, fast trials, slow trials, fast or slow trials, 
fast or slow or error trials, and minimum number of trials per condition that are not excluded.
the prc variables are the same, but per parcel.
debug = if 1, then we do not drop all the exclusion variables, so the user could see them
*/

/************/
/***Helpers:*/

%macro excludeTrial(libIn, libOut, inData, outdata, trlRT, trlErr, excludeErr, minRT, maxRT, trlExc);
	data &libout..&outdata; set &libin..&indata; 
			fastTrial = (&trlRT < &minRT);slowTrial = (&trlRT > &maxRT);
			/*Trial is excluded if one it is too slow, too fast, or error (but only if requested to exclude error trials*/
			%if &excludeErr eq 1 %then %do; &trlExc = (slowTrial or fastTrial or (&trlErr eq 1));	%end;
			%else %do; &trlExc = (slowTrial or fastTrial);	%end;
			goodTrial = (&trlExc-1)*-1; *0 becomes 1, 1 becomes 0;
	run;
%mend excludeTrial;

%macro excludeParcel(lib, dataset, sessionID, parcelVar, prcSlowRate, prcFastRate, cndVar, trlErr, prcNGoodTrials, prcExc, 
prcFastSlowRate, prcFastSlowErrRate);
	**mean per parcel;
	*slow RTs rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR slowTrial; output out=meanSlow mean=_prcSlowRate; 
	CLASS &sessionID &parcelVar; RUN;
	data meanSlow; set meanSlow; _prcExcSlow=(_prcSlowRate > &prcSlowRate); 
	keep &sessionID &parcelVar _prcExcSlow _prcSlowRate; run;
	*fast RTs rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR fastTrial; output out=meanFast mean=_prcFastRate; 
	CLASS &sessionID &parcelVar; RUN;
	data meanFast; set meanFast; _prcExcFast=(_prcFastRate > &prcFastRate); 
	keep &sessionID &parcelVar _prcExcFast _prcFastRate; run;
	*error rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR &trlErr; output out=meanErr mean=_prcErrRate; 
	CLASS &sessionID &parcelVar; RUN;
	data meanErr; set meanErr; _prcExcErr=(_prcErrRate > &prcErrRate); 
	keep &sessionID &parcelVar _prcExcErr _prcErrRate; run;
	*number of good trials;
	PROC MEANS noprint DATA=&lib..&dataset N; VAR goodTrial; output out=nGood N=_NGood; where goodTrial=1;
	CLASS &sessionID &parcelVar &cndVar; RUN;
	data nGood; set nGood; _prcExcNGd=(_NGood < &prcNGoodTrials); 
	keep &sessionID &parcelVar _prcExcNGd; run;
	PROC MEANS noprint DATA=nGood mean; VAR _prcExcNGd; output out=gdRate mean=_gdRate; 
	CLASS &sessionID &parcelVar; RUN;
	data gdRate; set gdRate; _prcExcNGd=(_gdRate>0); /*if not all the conditions had enough good trials, than the whole session is not good*/
	keep &sessionID &parcelVar _prcExcNGd; run;
	**merge those data sets with the trials data set;
	PROC SORT DATA=&lib..&dataset; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=meanSlow; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=meanFast; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=meanErr; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=gdRate; BY &sessionID &parcelVar; RUN;
	*in the merge, one row for each of the mean data sets is duplicated to all the matching rows in that parcel in 
	the trial-data;
	data &lib..&dataset; merge &lib..&dataset meanSlow meanFast meanErr gdRate; by &sessionID &parcelVar;
		*set parcel exclusion;
		if &sessionid>0 and &parcelVar>0;
		&prcExc = _prcExcSlow or _prcExcFast or _prcExcErr or _prcExcNGd or 
				(_prcSlowRate+_prcFastRate) > &prcFastSlowRate or 
				(_prcSlowRate+_prcFastRate+_prcErrRate) > &prcFastSlowErrRate;
	run;
%mend excludeParcel;

%macro excludeSession(lib, dataset, sessionID, sidSlowRate, sidFastRate, trlErr, sidErrRate, cndVar, sidNGoodTrials, 
	sidFastSlowRate, sidFastSlowErrRate);
	%excludeSession2(lib=&libout, dataset=&outdata, datasetSs=tmpDSSS, sessionID=&sessionID, sidSlowRate=&sidSlowRate, sidFastRate=&sidFastRate, 
	trlErr=&trlErr, sidErrRate=&sidErrRate, cndVar=&cndVar, sidNGoodTrials=&sidNGoodTrials, 
	sidFastSlowRate=&sidFastSlowRate, sidFastSlowErrRate=&sidFastSlowErrRate);
	/*excludeSession is the previous version, that does not keep the by-session data*/
	proc datasets library=&libout; delete tmpDSSS;run;
%mend excludeSession;

%macro excludeSession2(lib, dataset, datasetSs, sessionID, sidSlowRate, sidFastRate, trlErr, sidErrRate, cndVar, sidNGoodTrials, 
	sidFastSlowRate, sidFastSlowErrRate);
	**mean per session;
	*slow RTs rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR slowTrial; output out=meanSlow mean=_sidSlowRate; 
	CLASS &sessionID; RUN;
	data meanSlow; set meanSlow; _sidExcSlow=(_sidSlowRate > &sidSlowRate); 
	keep &sessionID _sidExcSlow _sidSlowRate; run;
	*fast RTs rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR fastTrial; output out=meanFast mean=_sidFastRate; 
	CLASS &sessionID; RUN;
	data meanFast; set meanFast; _sidExcFast=(_sidFastRate > &sidFastRate); 
	keep &sessionID _sidExcFast _sidFastRate; run;
	*error rate;
	PROC MEANS noprint DATA=&lib..&dataset MEAN;	VAR &trlErr; output out=meanErr mean=_sidErrRate; 
	CLASS &sessionID; RUN;
	data meanErr; set meanErr; _sidExcErr=(_sidErrRate > &sidErrRate); 
	keep &sessionID _sidExcErr _sidErrRate; run;
	*number of good trials;
	PROC MEANS noprint DATA=&lib..&dataset N; VAR goodTrial; output out=nGood N=_NGood; where goodTrial=1;
	CLASS &sessionID &cndVar; RUN;
	data nGood; set nGood; _sidExcNGd=(_NGood < &sidNGoodTrials); if &sessionID>0 and (length(&cndVar)>1); /*NOTICE: this means that you cannot use one letter as a condition name. Why? Because for some reason empty conditions have length of 1*/
	keep &sessionID &cndVar _sidExcNGd; run;
	PROC MEANS noprint DATA=nGood mean; VAR _sidExcNGd; output out=gdRate mean=_gdRate; 
	CLASS &sessionID; RUN;
	data gdRate; set gdRate; _sidExcNGd=(_gdRate>0); /*if not all the conditions had enough good trials, than the whole session is not good*/
	keep &sessionID _sidExcNGd; run;
	**merge those data sets;
	PROC SORT DATA=meanSlow; BY &sessionID; RUN;
	PROC SORT DATA=meanFast; BY &sessionID; RUN;
	PROC SORT DATA=meanErr; BY &sessionID; RUN;
	PROC SORT DATA=gdRate; BY &sessionID; RUN;
	*save by-session data;
	data &lib..&datasetSs; merge meanSlow meanFast meanErr gdRate; by &sessionID;
		if &sessionid>0;
		&sidExc = _sidExcSlow or _sidExcFast or _sidExcErr or _sidExcNGd or
				(_sidSlowRate+_sidFastRate) > &sidFastSlowRate or 
				(_sidSlowRate+_sidFastRate+_sidErrRate) > &sidFastSlowErrRate;
	run;
	**merge those data sets with the trials data set;
	*in the merge, one row for each of the mean data sets is duplicated to all the matching rows in that session in 
	the trial-data;
	PROC SORT DATA=&lib..&dataset; BY &sessionID; RUN;
	PROC SORT DATA=&lib..&datasetSs; BY &sessionID; RUN;
	data &lib..&dataset; merge &lib..&dataset &lib..&datasetSs; by &sessionID;
	run;
%mend excludeSession2;

%macro exclude(libIn, libOut, indata, outdata, sessionID, parcelVar, cndVar, trlRT, trlErr, excludeErr, 
trlExc, sidExc, prcExc, minRT, maxRT, 
sidErrRate, sidFastRate, sidSlowRate, sidFastSlowRate, sidFastSlowErrRate, sidNGoodTrials, 
prcErrRate, prcFastRate, prcSlowRate, prcFastSlowRate, prcFastSlowErrRate, prcNGoodTrials, debug);

/*The trial level. Here we set fast, slow, exclude, and good for each trial. If one does not use this, 
then these should be set outside this macro*/
%if &trlExc ne -1 %then %do;
	%excludeTrial(libIn=&libIn, libOut=&libOut, inData=&inData, outdata=&outdata, trlRT=&trlRT, trlErr=&trlErr, 
	excludeErr=&excludeErr, minRT=&minRT, maxRT=&maxRT, trlExc=&trlExc);
%end;
/*The parcel level*/
%if &prcExc ne -1 %then %do;
	%excludeParcel(lib=&libOut, dataset=&outdata, sessionID=&sessionID, parcelVar=&parcelVar, 
	prcSlowRate=&prcSlowRate, prcFastRate=&prcFastRate, cndVar=&cndVar, trlErr=&trlErr, prcNGoodTrials=&prcNGoodTrials, 
	prcExc=&prcExc, prcFastSlowRate=&prcFastSlowRate, prcFastSlowErrRate=&prcFastSlowErrRate);
%end;
/*The session level*/
%if &sidExc ne -1 %then %do;
	%excludeSession(lib=&libout, dataset=&outdata, sessionID=&sessionID, sidSlowRate=&sidSlowRate, sidFastRate=&sidFastRate, 
	trlErr=&trlErr, sidErrRate=&sidErrRate, cndVar=&cndVar, sidNGoodTrials=&sidNGoodTrials, 
	sidFastSlowRate=&sidFastSlowRate, sidFastSlowErrRate=&sidFastSlowErrRate);
%end;

	data &libout..&outdata; set &libout..&outdata;
		%if &debug ne 1 %then %do;
				drop _prcExcSlow _prcExcFast _prcExcErr _prcSlowRate _prcFastRate _prcErrRate _prcExcNGd 
					_sidExcSlow _sidExcFast _sidExcErr _sidSlowRate _sidFastRate _sidErrRate goodTrial _sidExcNGd 
					slowTrial fastTrial;
		%end;
	run;

%mend exclude;

/****************exclusion macro that also provides the session data*/

%macro exclude2(libIn, libOut, indata, outdata, outDataSs, sessionID, cndVar, trlRT, 
trlErr, excludeErr, trlExc, sidExc, minRT, maxRT, 
sidErrRate, sidFastRate, sidSlowRate, sidFastSlowRate, sidFastSlowErrRate, sidNGoodTrials, debug);

/*The trial level. Here we set fast, slow, exclude, and good for each trial. If one does not use this, 
then these should be set outside this macro*/
%if &trlExc ne -1 %then %do;
	%excludeTrial(libIn=&libIn, libOut=&libOut, inData=&inData, outdata=&outdata, trlRT=&trlRT, trlErr=&trlErr, 
	excludeErr=&excludeErr, minRT=&minRT, maxRT=&maxRT, trlExc=&trlExc);
%end;
/*The session level*/
%if &sidExc ne -1 %then %do;
	%excludeSession2(lib=&libout, dataset=&outdata, dataSetSs=&outDataSs, sessionID=&sessionID, sidSlowRate=&sidSlowRate, sidFastRate=&sidFastRate, 
	trlErr=&trlErr, sidErrRate=&sidErrRate, cndVar=&cndVar, sidNGoodTrials=&sidNGoodTrials, 
	sidFastSlowRate=&sidFastSlowRate, sidFastSlowErrRate=&sidFastSlowErrRate);
%end;

	data &libout..&outdata; set &libout..&outdata;
		%if &debug ne 1 %then %do;
				drop _sidExcSlow _sidExcFast _sidExcErr _sidSlowRate _sidFastRate _sidErrRate goodTrial _sidExcNGd 
					slowTrial fastTrial;
		%end;
	run;

%mend exclude2;

/***GNAT exclusion macro.
The go-nogo nature of the GNAT requires special exclusion in the trial level.
Specifically, we need exclude all trials that are not correct responses in go-trials. But, without considering 
the correct nogo trials as errors (in the error-rate calculations), but also count only the correct go trials as good 
trials.
*/
%macro excludeGNAT(libIn, libOut, indata, outdata, sessionID, parcelVar, cndVar, trlRT, trlErr, excludeErr, 
trlExc, sidExc, prcExc, minRT, maxRT, 
sidErrRate, sidFastRate, sidSlowRate, sidFastSlowRate, sidFastSlowErrRate, sidNGoodTrials, 
prcErrRate, prcFastRate, prcSlowRate, prcFastSlowRate, prcFastSlowErrRate, prcNGoodTrials, debug);

/**Set trial exclusion variables here**/
data &libout..&outdata; set &libin..&indata; 
	slowTrial = (&trlRT < &minRT);fastTrial = (&trlRT > &maxRT);
	tTmpErr = &trlErr; 
	if tTmpErr = 1 and &excludeErr = 1 then &trlExc = 1; /*exclude go errors*/
	else if tTmpErr > 1 then do;
		&trlExc = 1; /*exclude all no-go trials*/
		/*2 means incorrect no go, 3 means correct no-go, so merge them with the correct (0) and incorrect (1) of the go trials*/
		tTmpErr = (tTmpErr-3)*-1; /*3 becomes 0, 2 becomes 1*/
	end;
	else if &trlExc ne 1 then do;
		/*In correct trials, only slow and fast responses are excluded (of course)*/
		&trlExc = (slowTrial or fastTrial);	
	end;
	goodTrial = (&trlExc-1)*-1; *0 becomes 1, 1 becomes 0;
run;
/**Run the exclusion marco for parcels and sessions only. Not for trials. 
The macro will use the trial values that were set here.
So, for errors, it will treat correct go and correct no-go as correct.
For fast and slow, it will use both correct go and incorrect no-goes.
For good trials, it will use only correct responses that are not too slow or too fast.
**/
/*Note: libin and indata are used only in the trial-level, which will be skipped here (trlExc=-1)*/
%exclude(libIn=&libOut, libOut=&libOut, indata=&outdata, outdata=&outdata, sessionID=&sessionID, parcelVar=&parcelVar, 
cndVar=&cndVar, trlRT=&trlRT, trlErr=tTmpErr, excludeErr=&excludeErr, trlExc=-1, sidExc=&sidExc, prcExc=&prcExc, 
minRT=&minRT, maxRT=&maxRT, 
sidErrRate=&sidErrRate, sidFastRate=&sidFastRate, sidSlowRate=&sidSlowRate, sidFastSlowRate=&sidFastSlowRate, 
sidFastSlowErrRate=&sidFastSlowErrRate, sidNGoodTrials=&sidNGoodTrials, prcErrRate=&prcErrRate, prcFastRate=&prcFastRate, 
prcSlowRate=&prcSlowRate, prcFastSlowRate=&prcFastSlowRate, prcFastSlowErrRate=&prcFastSlowErrRate, 
prcNGoodTrials=&prcNGoodTrials, debug=&debug);


%mend excludeGNAT;

/***
Exclusion macro for the AMP. The exclusion is only according to the ratio of pleasant/unpleasant responses.
There are variable for the most extreme ratio for parcel or session (ratioSid and ratioPrc)
And variable for the most extreme ratio for parcel and session for each condition (ratioPerCondSID, ratioPerCondPrc).
*/
%macro ampExclude(libIn, libOut, indata, outdata, sessionID, parcelVar, cndVar, trlRsp, sidExc, prcExc, 
ratioPerCondSID, ratioPerCondPrc, ratioSid, ratioPrc, debug);

data &libout..&outdata; set &libin..&indata; run;

%if &sessionID ne -1 %then %do;
	/*Check whether there are too many pleasant or unpleasant responses in the session*/
	PROC MEANS noprint DATA=&libin..&indata MEAN; VAR &trlRsp; output out=meanP mean=_pRatioS; 
	CLASS &sessionID; RUN;
	data meanP; set meanP; _sidExcP=(_pRatioS > &ratioSid) or ((1-_pRatioS) > &ratioSid); 
	keep &sessionID _pRatioS _sidExcP; run;
	/*Check whether there are too many pleasant or unpleasant responses in the session for each condition*/
	PROC MEANS noprint DATA=&libin..&indata MEAN; VAR &trlRsp; output out=meanPC mean=_pRatioC; 
	CLASS &sessionID &cndVar; RUN;
	data meanPC; set meanPC; _sidExcP=(_pRatioC > &ratioPerCondSID) or ((1-_pRatioC) > &ratioPerCondSID); 
	keep &sessionID _sidExcP; run;
	/*if there is exclusion in one condition, then all the session should be excluded. So compute the mean of exclusions, across conditions*/
	PROC MEANS noprint DATA=meanpc MEAN; VAR _sidExcP; output out=meanPC mean=_sidExcP; 
	CLASS &sessionID; RUN;
	data meanPC; set meanPC; _sidExcPC=(_sidExcP>0); /*0 means that there was no exclusion in any of the conditions*/
	keep &sessionID _sidExcPC; run;
	/*Into the output data: add these two exclusion variables to the input data*/
	PROC SORT DATA=meanP; BY &sessionID; RUN;
	PROC SORT DATA=meanPC; BY &sessionID; RUN;
	PROC SORT DATA=&libin..&indata; BY &sessionID; RUN;
	/* the output data is merged with the exclusion data, and the exclusion sid variable is being set*/
	data &libout..&outdata; merge &libout..&outdata meanP meanPC; by &sessionID; 
		if &sessionID > 0; 
		&sidExc = _sidExcP or _sidExcPC; /*if one is violated, then the session is excluded*/
	RUN;
%end;
%if &parcelVar ne -1 %then %do;
	/*Check whether there are too many pleasant or unpleasant responses in the parcel*/
	PROC MEANS noprint DATA=&libin..&indata MEAN; VAR &trlRsp; output out=meanP mean=_pRatio; 
	CLASS &sessionID &parcelVar; RUN;
	data meanP; set meanP; _prcExcP=(_pRatio > &ratioPrc) or ((1-_pRatio) > &ratioPrc); 
	keep &sessionID &parcelVar _prcExcP; run;
	/*Check whether there are too many pleasant or unpleasant responses in the parcel for each condition*/
	PROC MEANS noprint DATA=&libin..&indata MEAN; VAR &trlRsp; output out=meanPC mean=_pRatio; 
	CLASS &sessionID &parcelVar &cndVar; RUN;
	data meanPC; set meanPC; _prcExcP=(_pRatio > &ratioPerCondSID) or ((1-_pRatio) > &ratioPerCondSID); 
	keep &sessionID &parcelVar _prcExcP; run;
	/*if there is exclusion in one condition, then all the parcel should be excluded. So compute the mean of exclusions, across conditions*/
	PROC MEANS noprint DATA=meanpc MEAN; VAR _prcExcP; output out=meanPC mean=_prcExcP; 
	CLASS &sessionID &parcelVar; RUN;
	data meanPC; set meanPC; _prcExcPC=(_prcExcP>0); /*0 means that there was no exclusion in any of the conditions*/
	keep &sessionID &parcelVar _prcExcPC; run;
	/*Into the output data: add these two exclusion variables to the input data*/
	PROC SORT DATA=meanP; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=meanPC; BY &sessionID &parcelVar; RUN;
	PROC SORT DATA=&libin..&indata; BY &sessionID &parcelVar; RUN;
	/* the output data is merged with the exclusion data, and the exclusion sid variable is being set*/
	data &libout..&outdata; merge &libout..&outdata meanP meanPC; by &sessionID &parcelVar; 
		if &sessionID > 0 and length(&parcelVar>1);
		&prcExc = _prcExcP or _prcExcPC; /*if one is violated, then the session is excluded*/
	RUN;
%end;
%if &debug ne 1 %then %do;
	data &libout..&outdata; set &libout..&outdata; drop _sidExcP _sidExcPC _prcExcP _prcExcPC _pRatioS; 
	if length(&cndVar)>1; run;
%end;
%mend ampExclude;

/***Speeded has its own exclusion macro
*/
%macro spdExclude(libIn, libOut, indata, outData, sessionID, parcelVar, cndVar, trlRsp, trlRT, trlExc, sidExc, prcExc, 
minRT, sidFastRate, sidSlowRate, sidPerCond, prcFastRate, prcSlowRate, prcPerCond, debug);
%if trlExc ne -1 %then %do;
	data &libOut..&outData; set &libIn..&inData;
		fastTrial = (&trlRT < &minRT);
		slowTrial = (&trlRsp=999); /*999 was the error-code for late responses*/
		&trlExc = (slowTrial or fastTrial);
		goodTrial = (&trlExc-1)*-1; *0 becomes 1, 1 becomes 0;
	run;
%end
%if (&prcExc ne -1) or (&sidExc ne -1) %then %do;
	data &libOut..&outData; set &libOut..&outData;		trlErr=0;	run; /*dummy error variable*/
	/*call the exclude macro but with skipping the trial part, 
	and with the dummy error variable, and with dummy 1 maximum rates for 
	errors and for slow+fast rates, because speeded does not have those. */
	%exclude(libIn=&libOut, libOut=&libOut, indata=&outData, outdata=&outData, sessionID=&sessionID, parcelVar=&parcelVar, 
			cndVar=&cndVar, trlRT=&trlRT, trlErr=trlErr, excludeErr=0, trlExc=-1, sidExc=&sidExc, prcExc=&prcExc, 
			minRT=&minRT, maxRT=1000, sidErrRate=1, sidFastRate=&sidFastRate, sidSlowRate=&sidSlowRate, 
			sidFastSlowRate=1, sidFastSlowErrRate=1, sidNGoodTrials=&sidPerCond, prcErrRate=1, 
			prcFastRate=&prcFastRate, prcSlowRate=&prcSlowRate, prcFastSlowRate=1, prcFastSlowErrRate=1, 
			prcNGoodTrials=&prcPerCond, debug=&debug);
	data &libOut..&outData; set &libOut..&outData;	drop trlErr; run; /*dump dummy error variable*/
%end;

%mend spdExclude;
